/**
 * Copyright (c) 2021 OceanBase
 * OceanBase CE is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#define USING_LOG_PREFIX SQL_ENG
#include "sql/resolver/cmd/ob_kill_stmt.h"
#include "sql/engine/ob_exec_context.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/code_generator/ob_expr_generator_impl.h"
#include "sql/engine/ob_physical_plan.h"

namespace oceanbase {
using namespace common;
namespace sql {

OB_SERIALIZE_MEMBER(ObKillSessionArg, sess_id_, tenant_id_, user_id_, is_query_, has_user_super_privilege_);

int ObKillSessionArg::init(ObExecContext& ctx, const ObKillStmt& stmt)
{
  int ret = OB_SUCCESS;
  ObSQLSessionInfo* session = NULL;
  if (OB_ISNULL(session = ctx.get_my_session())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is NULL", K(ret), K(ctx));
  } else if (OB_FAIL(calculate_sessid(ctx, stmt))) {
    LOG_WARN("fail to calculate sessid", K(ret), K(ctx), K(stmt));
  } else {
    tenant_id_ = session->get_priv_tenant_id();
    user_id_ = session->get_user_id();
    is_query_ = stmt.is_query();
    has_user_super_privilege_ = session->has_user_super_privilege();
  }
  return ret;
}

int ObKillSessionArg::calculate_sessid(ObExecContext& ctx, const ObKillStmt& stmt)
{
  int ret = OB_SUCCESS;
  ObSQLSessionInfo* my_session = ctx.get_my_session();
  ObPhysicalPlanCtx* plan_ctx = ctx.get_physical_plan_ctx();
  ObPhysicalPlan phy_plan;
  ObPhysicalPlanCtx phy_plan_ctx(ctx.get_allocator());
  ObExecContext* exec_ctx = NULL;  // because ObExecContext is bigger than 10K, can't use it as local variable
  ObExprCtx expr_ctx;
  ObNewRow tmp_row;
  RowDesc row_desc;
  ObExprGeneratorImpl expr_gen(0, 0, NULL, row_desc);
  ObObj value_obj;
  ObRawExpr* value_expr = NULL;
  void* tmp_ptr = NULL;
  if (OB_ISNULL(my_session) || OB_ISNULL(plan_ctx)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("data member from ObExecContext is Null", K(ret), K(my_session), K(plan_ctx));
  } else {
    ObArenaAllocator allocator(
        common::ObModIds::OB_SQL_EXPR_CALC, OB_MALLOC_NORMAL_BLOCK_SIZE, my_session->get_effective_tenant_id());
    ObSqlExpression sql_expr(allocator, 0);
    const int64_t cur_time =
        plan_ctx->has_cur_time() ? plan_ctx->get_cur_time().get_timestamp() : ObTimeUtility::current_time();
    phy_plan_ctx.set_cur_time(cur_time, *my_session);

    if (OB_UNLIKELY(NULL == (tmp_ptr = allocator.alloc(sizeof(ObExecContext))))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_ERROR("fail to alloc ObExecContext", K(ret));
    } else {
      exec_ctx = new (tmp_ptr) ObExecContext;
      phy_plan_ctx.set_phy_plan(&phy_plan);
      expr_ctx.phy_plan_ctx_ = &phy_plan_ctx;
      expr_ctx.my_session_ = my_session;
      expr_ctx.exec_ctx_ = exec_ctx;
      expr_ctx.calc_buf_ = &allocator;
      if (OB_ISNULL(value_expr = stmt.get_value_expr())) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("fail to get value expr", K(ret), K(value_expr));
      } else if (OB_FAIL(expr_gen.generate(*value_expr, sql_expr))) {
        LOG_WARN("fail to fill sql expression", K(ret));
      } else if (FALSE_IT(phy_plan.set_regexp_op_count(expr_gen.get_cur_regexp_op_count()))) {
        // will not reach here
      } else if (FALSE_IT(phy_plan.set_like_op_count(expr_gen.get_cur_like_op_count()))) {
        // will not reach here
      } else if (OB_FAIL(sql_expr.calc(expr_ctx, tmp_row, value_obj))) {
        LOG_WARN("fail to calc value", K(ret), K(stmt.get_value_expr()));
      } else {
        const ObObj* res_obj = NULL;
        EXPR_DEFINE_CAST_CTX(expr_ctx, CM_WARN_ON_FAIL);
        EXPR_CAST_OBJ_V2(ObUInt32Type, value_obj, res_obj);
        ret = OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD == ret ? OB_SUCCESS : ret;
        if (OB_FAIL(ret)) {
          LOG_WARN(
              "fail to cast expr", "orig type", value_obj.get_type(), "dest type", "ObUint32type", K(ret), K(res_obj));
        } else if (OB_ISNULL(res_obj)) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN(
              "fail to cast expr", "orig type", value_obj.get_type(), "dest type", "ObUint32type", K(ret), K(res_obj));
        } else {
          sess_id_ = res_obj->get_uint32();
        }
      }
    }
  }
  return ret;
}

}  // namespace sql
}  // namespace oceanbase
